/******************************************************************************\
**  filename:   ScanList.c
**  author:     tianjun
**  created:    2007-4-17 13:00
\******************************************************************************/
#include <NetworkManagement_Private.h>
#include <mainthread.h>
#include <spr.h>
#include <config.h>
#include <configPriority.h>
#include <configFlashRecordId.h>
#include <debuglog.h>

#include <wlanScan.h>
#include <nethwApi.h>
#include <simpleconfig.h>
//========================================================================
// Local defines and datas
//========================================================================
#define SCANLIST_SCAN_TIMEOUT_SECONDS       10
#define SCANLIST_SCAN_TIMEOUT_SECONDS_WPS   120
#define SCANLIST_COMPLETE_CALLBACK_MAX      4

typedef struct _ScanList_Management_
{
    bool                    isInit;

    ScanList_Scan_WPS_Mode  eWPSMode;

    unsigned                networkCount;
    nethwWlanNetworkInfo    *networkP[CFG_WLAN_SCAN_MAX];
    nethwWlanNetworkInfo    networkCopy[CFG_WLAN_SCAN_MAX];

    struct pollerTask       stateEventTask;
    struct pollerTask       scanEventTask;
    struct pollerTask       scanTimeoutEventTask;

    bool                    isScanInProgress;

    fnScanCompleteCB        *scanCompleteCBArray[SCANLIST_COMPLETE_CALLBACK_MAX];
    fnStateEventCB          *stateEventCB;

} ScanList_Management;

static ScanList_Management ScanListM_s;

//========================================================================
// Private Routines
//========================================================================
static int ScanList_ScanCompare(nethwWlanNetworkInfo **p1, nethwWlanNetworkInfo **p2)
{
    int n;

    if ((*p1)->ssid[0]  && !(*p2)->ssid[0]) return 1;
    if (!(*p1)->ssid[0] &&  (*p2)->ssid[0]) return -1;
    if ((n = strcasecmp((*p1)->ssid, (*p2)->ssid)))
    {
        return n;
    }
    return (*p1)->rssi - (*p2)->rssi;
}

static void ScanList_ScanGetSort()
{
    ScanListM_s.networkCount = 0;

    for (int i=0; i<CFG_WLAN_SCAN_MAX; i++)
    {
        const nethwWlanNetworkInfo *networkP = wlanScanScanGet(i);
        if (!networkP || networkP->ssid[0] == '\0')
        {
            continue;
        }

        if (ScanListM_s.eWPSMode != ScanList_Scan_WPS_Mode_eOff) // if we start a WPS scan?
        {
            struct simpleconfigParseBeaconWpsIeStruct WpsInfo;
            simpleconfigParseBeaconWpsIe( &WpsInfo, networkP->beacon.ie_wps );

            if (WpsInfo.SelectedRegistrar == 0) continue; // so we ignore no WPS AP

            if (ScanListM_s.eWPSMode == ScanList_Scan_WPS_Mode_ePushButton)
            {
                if (WpsInfo.PushButton == 0) continue; // so we ignore WPS AP with PIN Mode
            }
            else if (ScanListM_s.eWPSMode == ScanList_Scan_WPS_Mode_ePIN)
            {
                if (WpsInfo.PushButton != 0) continue; // so we ignore WPS AP with PushButton Mode
            }
        }

        ScanListM_s.networkCopy[ScanListM_s.networkCount] = *networkP;
        ScanListM_s.networkP[ScanListM_s.networkCount] =
                &ScanListM_s.networkCopy[ScanListM_s.networkCount];

        ScanListM_s.networkCount++;
    }

    debugf("## NM: Scaned %d APs.\n", ScanListM_s.networkCount);

    qsort(
        ScanListM_s.networkP,
        ScanListM_s.networkCount,
        sizeof(ScanListM_s.networkP[0]),
        (void*)ScanList_ScanCompare
        );
}

static void ScanList_StateEventHandler(struct pollerTask *taskP)
{
    debugf("## NM: State Event recv.\n");

    if (ScanListM_s.stateEventCB)
    {
        ScanListM_s.stateEventCB(NULL);
    }
}

static void ScanList_ScanEventHandler(struct pollerTask *taskP)
{
    if (ScanListM_s.isScanInProgress)
    {
        debugf("## NM: Scan Event recv.\n");

        ScanList_ScanGetSort();

        if (ScanListM_s.networkCount > 0)
        {
            ScanList_ScanStop((void *)SCAN_STATUS_SUCCESS);
        }
        else
        {
            if (ScanListM_s.eWPSMode == ScanList_Scan_WPS_Mode_ePushButton)
            {
                nethwWlanScanStart(nethwWlanScanFlag_Wps); // Scan again until timeout.
            }
            else
            {
                ScanList_ScanStop((void *)SCAN_STATUS_FAILURE);
            }
        }
    }
}

static void ScanList_ScanTimeoutEventHandler(struct pollerTask *taskP)
{
    if (ScanListM_s.isScanInProgress)
    {
        debugf("## NM: Scan timed out.\n");

        nethwWlanScanStop();

        ScanList_ScanGetSort();

        if (ScanListM_s.networkCount > 0)
        {
            ScanList_ScanStop((void *)SCAN_STATUS_SUCCESS);
        }
        else
        {
            ScanList_ScanStop((void *)SCAN_STATUS_TIMEOUT);
        }
    }
}
//========================================================================
// Public Routines
//========================================================================
/*
 *(Scan function interface)
 */
void ScanList_Initialize()
{
    if (ScanListM_s.isInit) return;
    ScanListM_s.isInit = true;

    ScanListM_s.stateEventCB = NULL;

    mainthreadTaskInit(
        &ScanListM_s.stateEventTask,
        "ScanList_StateEventTask",
        ScanList_StateEventHandler
        );
    pollerTaskPrioritySet(
        &ScanListM_s.stateEventTask,
        CONFIG_PRIORITY_DEFAULT
        );

    mainthreadTaskInit(
        &ScanListM_s.scanEventTask,
        "ScanList_ScanEventTask",
        ScanList_ScanEventHandler
        );
    pollerTaskPrioritySet(
        &ScanListM_s.scanEventTask,
        CONFIG_PRIORITY_DEFAULT
        );

    mainthreadTaskInit(
        &ScanListM_s.scanTimeoutEventTask,
        "ScanList_ScanTimeoutEventTask",
        ScanList_ScanTimeoutEventHandler
        );
    pollerTaskPrioritySet(
        &ScanListM_s.scanTimeoutEventTask,
        CONFIG_PRIORITY_DEFAULT
        );

    ScanListM_s.eWPSMode = ScanList_Scan_WPS_Mode_eOff;
    ScanListM_s.networkCount = 0;
    ScanListM_s.isScanInProgress = false;

    nethwWlanStateEventTaskSet(
        &ScanListM_s.stateEventTask,
        &ScanListM_s.scanEventTask
        );
}

void ScanList_Release()
{
    ScanList_ScanStop((void *)SCAN_STATUS_ABORT);
}

bool ScanList_WPSScanStart(fnScanCompleteCB *scanCompleteCB, ScanList_Scan_WPS_Mode eMode)
{
    ScanListM_s.eWPSMode = eMode;
    return ScanList_ScanStart(scanCompleteCB);
}

bool ScanList_ScanStart(fnScanCompleteCB *scanCompleteCB)
{
    int i = 0;
    unsigned int ScanFlag = 0;

    // Install scanCompleteCB
    //
    for (i = 0; i < SCANLIST_COMPLETE_CALLBACK_MAX; i++)
    {
        if (ScanListM_s.scanCompleteCBArray[i] == NULL)
        {
            ScanListM_s.scanCompleteCBArray[i] = scanCompleteCB;
            break;
        }
    }

    if (i == SCANLIST_COMPLETE_CALLBACK_MAX)
    {
        return false;
    }

    if (ScanListM_s.isScanInProgress)
    {
        return false;
    }

//    ScanListM_s.networkCount = 0;

    debugf("## NM: Scan started.\n");
    ScanListM_s.isScanInProgress = true;

    switch (ScanListM_s.eWPSMode)
    {
        case ScanList_Scan_WPS_Mode_ePushButton :
            ScanFlag = nethwWlanScanFlag_Wps;
            break;
        case ScanList_Scan_WPS_Mode_ePIN :
            ScanFlag = nethwWlanScanFlag_Wps | nethwWlanScanFlag_WpsPinOnly;
            break;
        case ScanList_Scan_WPS_Mode_eOff :
        default:
            ScanFlag = 0;
            break;
    }
    
    nethwWlanScanStart(ScanFlag);

    if (ScanListM_s.eWPSMode != ScanList_Scan_WPS_Mode_ePushButton)
    {
        pollerTaskLongTimeoutRelSeconds(
            &ScanListM_s.scanTimeoutEventTask,
            SCANLIST_SCAN_TIMEOUT_SECONDS
            );
    }
    else
    {
        pollerTaskLongTimeoutRelSeconds(
            &ScanListM_s.scanTimeoutEventTask,
            SCANLIST_SCAN_TIMEOUT_SECONDS_WPS
            );
    }

    return true;
}

void ScanList_ScanStop(void *arg)
{
    if (ScanListM_s.isScanInProgress)
    {
        debugf("## NM: Scan Stopped.\n");
        nethwWlanScanStop();
        ScanListM_s.isScanInProgress = false;

        for (int i = 0; i < SCANLIST_COMPLETE_CALLBACK_MAX; i++)
        {
            if (ScanListM_s.scanCompleteCBArray[i] != NULL)
            {
                fnScanCompleteCB *LocalScanCompleteCB = NULL;

                LocalScanCompleteCB = ScanListM_s.scanCompleteCBArray[i];
                ScanListM_s.scanCompleteCBArray[i] = NULL;

                LocalScanCompleteCB(arg);
            }
        }
    }

    ScanListM_s.eWPSMode = ScanList_Scan_WPS_Mode_eOff;
}

void ScanList_InstallStateEvent(fnStateEventCB *stateEventCB)
{
    ScanListM_s.stateEventCB = stateEventCB;
}

/*
 *(Get scan list item content)
 */
unsigned ScanList_GetItemsNumber()
{
    return ScanListM_s.networkCount;
}

void *ScanList_GetItemNetworkP(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem];
}

unsigned short ScanList_GetItemChannelNumber(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->chanNumber;
}

unsigned short ScanList_GetItemChannelMHz(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->chanMHz;
}

unsigned short ScanList_GetItemSecurityMode(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->SecurityMode;
}

unsigned short ScanList_GetItemNetworkType(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->NetworkType;
}

unsigned short ScanList_GetItemRSSI(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->rssi;
}

void ScanList_GetItemBSSID(unsigned iItem, unsigned char bssid[6])
{
    assert(iItem < ScanListM_s.networkCount);

    memcpy(
        bssid,
        ScanListM_s.networkP[iItem]->bssid,
        sizeof(ScanListM_s.networkP[iItem]->bssid)
        );
}

char* ScanList_GetItemSSID(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->ssid;
}

unsigned char* ScanList_GetItemWPSBeacomIE(unsigned iItem)
{
    assert(iItem < ScanListM_s.networkCount);

    return ScanListM_s.networkP[iItem]->beacon.ie_wps;
}
